package com.google.code.retrobox.editor.tiles;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.google.code.retrobox.editor.TestSet;
import com.google.code.retrobox.editor.dataCollections.TileSet;

public class TilesIO {
	public void exportTileSetAsHeader(File file, TileEditor tileEditor)
			throws Exception {

		String path = file.getAbsolutePath();
		if (!path.endsWith(".h")) {
			path = path + ".h";
		}

		BufferedWriter out = new BufferedWriter(new FileWriter(path));
		out
				.write("// Auto generated tileset header file by Retrobox editor \n\n"
						+

						"// Tileset properties: \n"
						+

						"// Width: "
						+ Tile.WIDTH
						+ "\n"
						+ "// Height: "
						+ Tile.HEIGHT
						+ "\n"
						+ "// Tile count: "
						+ tileEditor.getTileset().getLength() + "\n\n" +

						"#define TILESET { \\\n");

		int i = 1, j = 0;
		int len = Tile.TILE_BPP * Tile.HEIGHT;
		for (char element : tileEditor.getTileset().getRawData()) {
			if (j % len == 0) {
				out.write(" /* block : " + String.format("0x%X, ", j / len)
						+ " */ \\\n");
			}
			out.write(String.format("0x%X, ", (int) element));
			if (i == Tile.TILE_BPP) {
				out.write("\\\n");
				i = 0;
			}
			i++;
			j++;
		}

		out.write("}\n\n");

		// --------------- Collision data --------------------- //
		out.write("#define COLLDATA { \\\n");

		i = 1;
		for (char element : tileEditor.getTileset().getRawCollisionData()) {
			out.write(String.format("0x%X, ", (int) element));
			if (i == 8) {
				out.write("\\\n");
				i = 0;
			}
			i++;
		}
		out.write("}\n");
		// ---------------------------------------------------- //

		out.close();
	}

	public void exportTileSetAsGameduinoHeader(File file, TileEditor tileEditor)
			throws Exception {

		String path = file.getAbsolutePath();
		if (!path.endsWith(".h")) {
			path = path + ".h";
		}

		BufferedWriter out = new BufferedWriter(new FileWriter(path));
		out
				.write("// Auto generated tileset header file by Retrobox editor \n\n"
						+

						"// Tileset properties: \n"
						+

						"// Width: "
						+ Tile.WIDTH
						+ "\n"
						+ "// Height: "
						+ Tile.HEIGHT
						+ "\n"
						+ "// Tile count: "
						+ tileEditor.getTileset().getLength() + "\n\n" +

						"#define TILESET { \\\n");

		int i = 1, j = 0;
		int len = Tile.TILE_BPP * Tile.HEIGHT;
		for (char element : tileEditor.getTileset().getGameduinoRawData()) {
			if (j % len == 0) {
				out.write(" /* block : " + String.format("0x%X, ", j / len)
						+ " */ \\\n");
			}
			out.write(String.format("0x%X, ", (int) element));
			if (i == Tile.TILE_BPP) {
				out.write("\\\n");
				i = 0;
			}
			i++;
			j++;
		}

		out.write("}\n\n");
		out.close();
	}

	public void saveTileSet(File file, TileEditor tileEditor) throws Exception {
		String path = file.getAbsolutePath();
		if (!path.endsWith(".xml")) {
			path = path + ".xml";
			file = new File(path);
		}

		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.newDocument();

		Element root = doc.createElement("tileSet");
		root.setAttribute("version", "1.0");
		doc.appendChild(root);

		Element tileData = doc.createElement("tileData");
		tileData.setAttribute("width", String.valueOf(Tile.WIDTH));
		tileData.setAttribute("height", String.valueOf(Tile.HEIGHT));
		tileData.setAttribute("length", String.valueOf(tileEditor.getTileset()
				.getLength()));
		int i = 1, j = 0;
		int len = Tile.TILE_BPP * Tile.HEIGHT;
		StringBuffer sb = new StringBuffer();
		for (char element : tileEditor.getTileset().getRawData()) {
			// if (j % len == 0) {
			// out.write(" /* block : " + String.format("0x%X, ", j / len)
			// + " */ \\\n");
			// }
			sb.append(String.format("0x%X, ", (int) element));
			if (i == Tile.TILE_BPP) {
				sb.append("\\\n");
				i = 0;
			}
			i++;
			j++;
		}
		tileData.setTextContent(sb.toString());
		root.appendChild(tileData);
		Element collisionData = doc.createElement("collisionData");
		i = 1;
		sb = new StringBuffer();
		for (char element : tileEditor.getTileset().getRawCollisionData()) {
			sb.append(String.format("0x%X, ", (int) element));
			if (i == 8) {
				sb.append("\\\n");
				i = 0;
			}
			i++;
		}
		collisionData.setTextContent(sb.toString());
		root.appendChild(collisionData);

		// write the content into xml file
		TransformerFactory transformerFactory = TransformerFactory
				.newInstance();
		Transformer transformer = transformerFactory.newTransformer();
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		DOMSource source = new DOMSource(doc);
		StreamResult result = new StreamResult(file);
		transformer.transform(source, result);
	}

	public TileSet importTileset(File file, TileEditor tileEditor)
			throws Exception {
		int n = 0;
		boolean dataFound = false;
		List<Character> data = new ArrayList<Character>();
		List<Character> colData = new ArrayList<Character>();

		FileInputStream fstream = new FileInputStream(file);
		DataInputStream in = new DataInputStream(fstream);
		BufferedReader br = new BufferedReader(new InputStreamReader(in));

		String line;
		while ((line = br.readLine()) != null) {
			Scanner sc = new Scanner(line);

			while (sc.hasNext() && n == 0) {
				if (sc.next().indexOf("count:") >= 0) {
					try {
						n = sc.nextInt();
					} catch (Exception exc) {
						throw new Exception(
								"Error parsing tile count from file!!!", exc);
					}
					break;
				}
			}

			if (n > 0) {
				while ((line = br.readLine()) != null) {
					if (line.indexOf("#define") >= 0 && !dataFound) {
						while ((line = br.readLine()) != null) {
							if (line.indexOf("/*") >= 0) {
								continue;
							}
							if (line.indexOf("}") >= 0) {
								dataFound = true;
								break;
							}
							sc = new Scanner(line);

							while (sc.hasNext()) {
								String next;
								if ((next = sc.next()).toLowerCase()
										.startsWith("0x")) {
									int lastIndex = next.length();
									int tmp = next.lastIndexOf(",");
									if (tmp > -1) {
										lastIndex = tmp;
									}
									try {
										data.add((char) (Integer.parseInt(next
												.substring(2, lastIndex), 16)));
									} catch (Exception e) {
										throw new Exception(
												"Error parsing hex from text file",
												e);
									}
								}
							}
						}
					}
					// --------- Collision data ------------ //
					if (line.indexOf("#define") >= 0 && dataFound) {
						while ((line = br.readLine()) != null) {
							if (line.indexOf("/*") >= 0) {
								continue;
							}
							if (line.indexOf("}") >= 0) {
								break;
							}
							sc = new Scanner(line);

							while (sc.hasNext()) {
								String next;
								if ((next = sc.next()).toLowerCase()
										.startsWith("0x")) {
									int lastIndex = next.length();
									int tmp = next.lastIndexOf(",");
									if (tmp > -1) {
										lastIndex = tmp;
									}
									try {
										colData.add((char) (Integer.parseInt(
												next.substring(2, lastIndex),
												16)));
									} catch (Exception e) {
										throw new Exception(
												"Error parsing hex from text file",
												e);
									}
								}
							}
						}

						break;
					}
					// ------------------------------------- //
				}
				break;
			}
		}
		in.close();

		char[] dataArray = new char[data.size()];

		for (int i = 0; i < data.size(); i++) {
			dataArray[i] = data.get(i);
		}

		TileSet ts;

		if (colData.size() > 0) {
			char[] colDataArray = new char[data.size()];

			for (int i = 0; i < colData.size(); i++) {
				colDataArray[i] = colData.get(i);
			}

			ts = new TileSet(dataArray, colDataArray);
		} else {
			ts = new TileSet(dataArray, TestSet.COLLDATA);
		}

		return ts;

	}

	public TileSet loadTileSet(File file, TileEditor tileEditor)
			throws Exception {
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();
		Document doc = db.parse(file);
		doc.getDocumentElement().normalize();
		System.out.println("Root element "
				+ doc.getDocumentElement().getNodeName());
		if (!doc.getDocumentElement().getNodeName().equals("tileSet")) {
			throw new Exception("Unknown file format, illegal tag: "
					+ doc.getDocumentElement().getNodeName());
		}
		if (!doc.getDocumentElement().getAttribute("version").equals("1.0")) {
			throw new Exception("Unsupported file version "
					+ doc.getDocumentElement().getAttribute("version"));
		}

		NodeList list = doc.getElementsByTagName("tileData");
		List<Character> data = new ArrayList<Character>();
		if (list.getLength() == 1) {
			String tileData = list.item(0).getTextContent();
			System.out.println(tileData);
			int length = 0;
			if ((((Element) list.item(0)).getAttribute("length") == "")) {
				throw new Exception("Missing length attribute."
						+ doc.getDocumentElement().getAttribute("length"));
			} else {
				length = Integer.parseInt(((Element) list.item(0))
						.getAttribute("length"));
			}
			Scanner sc = new Scanner(tileData);

			while (sc.hasNext()) {
				String next;
				if ((next = sc.next()).toLowerCase().startsWith("0x")) {
					int lastIndex = next.length();
					int tmp = next.lastIndexOf(",");
					if (tmp > -1) {
						lastIndex = tmp;
					}
					try {
						data.add((char) (Integer.parseInt(next.substring(2,
								lastIndex), 16)));
					} catch (Exception e) {
						throw new Exception("Error parsing hex from file", e);
					}
				}
			}
			list = doc.getElementsByTagName("collisionData");
			List<Character> colData = new ArrayList<Character>();
			if (list.getLength() == 1) {
				String collisiondata = list.item(0).getTextContent();
				sc = new Scanner(collisiondata);

				while (sc.hasNext()) {
					String next;
					if ((next = sc.next()).toLowerCase().startsWith("0x")) {
						int lastIndex = next.length();
						int tmp = next.lastIndexOf(",");
						if (tmp > -1) {
							lastIndex = tmp;
						}
						try {
							colData.add((char) (Integer.parseInt(next
									.substring(2, lastIndex), 16)));
						} catch (Exception e) {
							throw new Exception("Error parsing hex from file",
									e);
						}
					}
				}
				char[] dataArray = new char[data.size()];

				for (int i = 0; i < data.size(); i++) {
					dataArray[i] = data.get(i);
				}

				TileSet ts;

				if (colData.size() > 0) {
					char[] colDataArray = new char[data.size()];

					for (int i = 0; i < colData.size(); i++) {
						colDataArray[i] = colData.get(i);
					}

					ts = new TileSet(dataArray, colDataArray);
				} else {
					ts = new TileSet(dataArray, TestSet.COLLDATA);
				}

				return ts;
			} else {
				throw new Exception("Illegal format");
			}
		} else {
			throw new Exception("Illegal format");
		}

	}

}
